﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using FlexLib.FileTree;
using FlexLib.RegistryTree;
using FlexLib.Plugins;
using System.Security;

namespace PackageEditor
{
	public partial class EditorPanel : UserControl
	{
		private const string ROOT_NODE_TAG = "root";
		
		private const string BINARY_VALUE_TYPE = "Binary";
		private const string STRING_VALUE_TYPE = "String";
		private const string ENVSTRING_VALUE_TYPE = "Environmental string";
		private const string DWORD_VALUE_TYPE = "DWORD";
		private const string PLUGIN_EXTENSION = ".dll";

		private InstPackage package;
		private PackageEditor.EditorForm.WorkingModule module;
		private TreeNode rootNode;
		private TreeNode rootKey;

		private void AddSpecialFolders(TreeNode rootNode)
		{
			TreeNode node = new TreeNode("Plugin directory");
			node.Tag = (rootNode.Tag as FileTreeNode).AddNode("Plugin directory");
			rootNode.Nodes.Add(node);

			node = new TreeNode("Common directory");
			node.Tag = (rootNode.Tag as FileTreeNode).AddNode("Common directory");
			rootNode.Nodes.Add(node);

			node = new TreeNode("Windows directory");
			node.Tag = (rootNode.Tag as FileTreeNode).AddNode("Windows directory");
			rootNode.Nodes.Add(node);

			node = new TreeNode("System directory");
			node.Tag = (rootNode.Tag as FileTreeNode).AddNode("System directory");
			rootNode.Nodes.Add(node);
			
			node = new TreeNode("Fonts directory");
			node.Tag = (rootNode.Tag as FileTreeNode).AddNode("Fonts directory");
			rootNode.Nodes.Add(node);
		}

		private void AddSpecialKeys(TreeNode rootKey)
		{
			TreeNode node = new TreeNode("HKEY_CLASSES_ROOT");
			node.Tag = (rootKey.Tag as RegTreeKey).AddKey("HKEY_CLASSES_ROOT");
			rootKey.Nodes.Add(node);
			
			node = new TreeNode("HKEY_CURRENT_USER");
			node.Tag = (rootKey.Tag as RegTreeKey).AddKey("HKEY_CURRENT_USER");
			rootKey.Nodes.Add(node);

			node = new TreeNode("HKEY_LOCAL_MACHINE");
			node.Tag = (rootKey.Tag as RegTreeKey).AddKey("HKEY_LOCAL_MACHINE");
			rootKey.Nodes.Add(node);

			node = new TreeNode("HKEY_USERS");
			node.Tag = (rootKey.Tag as RegTreeKey).AddKey("HKEY_USERS");
			rootKey.Nodes.Add(node);

			node = new TreeNode("HKEY_CURRENT_CONFIG");
			node.Tag = (rootKey.Tag as RegTreeKey).AddKey("HKEY_CURRENT_CONFIG");
			rootKey.Nodes.Add(node);
		}

		public void AddRootNodes()
		{
			FilesTree.Nodes.Add("File system");
			RegistryTree.Nodes.Add("Registry");
		}
		
		private void InitializeRootNodes()
		{
			rootNode = FilesTree.SelectedNode = FilesTree.Nodes[0];
			if (module == EditorForm.WorkingModule.Admin)
				rootNode.Tag = package.RootNodeAdmin;
			else
				rootNode.Tag = package.RootNodeUser;
			
			rootKey = RegistryTree.SelectedNode = RegistryTree.Nodes[0];
			if (module == EditorForm.WorkingModule.Admin)
				rootKey.Tag = package.RootKeyAdmin;
			else
				rootKey.Tag = package.RootKeyUser;
		}

		public EditorPanel()
		{
			InitializeComponent();
		}

		public void SetPackage(InstPackage package, PackageEditor.EditorForm.WorkingModule module)
		{
			this.package = package;
			this.module = module;

			InitializeRootNodes();
		}

		private void AddFolder_Click(object sender, EventArgs e)
		{
			if (FilesTree.SelectedNode == null || FilesTree.SelectedNode == rootNode) 
				return;			

			TreeNode node = FilesTree.SelectedNode.Nodes.Add("New folder");
			node.Tag = (FilesTree.SelectedNode.Tag as FileTreeNode).AddNode("New folder");
			FilesTree.SelectedNode.Expand();
			FilesTree.SelectedNode = node;
			node.BeginEdit();			
		}

		private void AddKey_Click(object sender, EventArgs e)
		{
			if (RegistryTree.SelectedNode == null || RegistryTree.SelectedNode == rootKey) return;

			TreeNode node = RegistryTree.SelectedNode.Nodes.Add("New key");
			node.Tag = (RegistryTree.SelectedNode.Tag as RegTreeKey).AddKey("New key");
			RegistryTree.SelectedNode.Expand();
			RegistryTree.SelectedNode = node;
			node.BeginEdit();
		}

		private void FilesContextMenu_Opening(object sender, CancelEventArgs e)
		{
			if (FilesTree.SelectedNode == null)
				return;

			if (FilesTree.SelectedNode == rootNode || FilesTree.SelectedNode == rootNode)
			{
				addToolStripMenuItem.Enabled = false;
				addSpecialFolderToolStripMenuItem.Enabled = true;
				deleteToolStripMenuItem.Enabled = false;
			}
			else
			{
				addToolStripMenuItem.Enabled = true;
				addSpecialFolderToolStripMenuItem.Enabled = false;
				deleteToolStripMenuItem.Enabled = true;
			}
		}

		private void RegistryContextMenu_Opening(object sender, CancelEventArgs e)
		{
			if (RegistryTree.SelectedNode == rootKey)
			{
				importToolStripMenuItem.Enabled = true;
				addToolStripMenuItem1.Enabled = false;
			}
			else
			{
				importToolStripMenuItem.Enabled = false;
				addToolStripMenuItem1.Enabled = true;
			}
		}

		private void ScanSelectedNode()
		{
			FilesView.Items.Clear();
			try
			{
				for (int i = 0; i < (FilesTree.SelectedNode.Tag as FileTreeNode).Files.Count; i++)
				{
					if (!Path.IsPathRooted((FilesTree.SelectedNode.Tag as FileTreeNode).Files[i]))
						(FilesTree.SelectedNode.Tag as FileTreeNode).Files[i] = Path.GetFullPath((FilesTree.SelectedNode.Tag as FileTreeNode).Files[i]);
					FilesView.Items.Add((FilesTree.SelectedNode.Tag as FileTreeNode).Files[i]);
				}
			}
			catch { }
		}

		private void FilesTree_AfterSelect(object sender, TreeViewEventArgs e)
		{
			ScanSelectedNode();
		}	

		private void RegistryTree_AfterSelect(object sender, TreeViewEventArgs e)
		{
			RegistryView.Items.Clear();
			try
			{	
				ListViewItem item;
				foreach (RegProperty property in (RegistryTree.SelectedNode.Tag as RegTreeKey).Properties)
				{
					item = new ListViewItem(property.Name);
					item.SubItems.Add(property.Value);
					item.SubItems.Add(property.Type.ToString());
					RegistryView.Items.Add(item);
				}
			}
			catch { }
		}

		private void RemoveSelectedNode()
		{
			if (FilesTree.SelectedNode != rootNode && FilesTree.SelectedNode.Parent != rootNode)
				if (MessageBox.Show("Are you sure?", "Deleting", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
				{
					(FilesTree.SelectedNode.Tag as FileTreeNode).Remove();
					FilesTree.SelectedNode.Remove();
				}
		}

		private void RemoveSelectedKey()
		{
			if (RegistryTree.SelectedNode != rootNode && RegistryTree.SelectedNode.Parent != rootNode)
				if (MessageBox.Show("Are you sure?", "Deleting", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
				{
					(RegistryTree.SelectedNode.Tag as RegTreeKey).Remove();
					RegistryTree.SelectedNode.Remove();
				}
		}

		private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
		{
			RemoveSelectedNode();
		}

		private void AddDLL(string filename, TreeNode node, PackageEditor.EditorForm.WorkingModule module)
		{
			string path = (node.Tag as FileTreeNode).GetNonRootedPath() + '\\' + Path.GetFileName(filename);
			switch (module)
			{
				case EditorForm.WorkingModule.Admin:
					package.AdminDllFiles.Add(path, filename);
					break;
				case EditorForm.WorkingModule.User:
					package.UserDllFiles.Add(path, filename);
					break;
			}
		}

		public void New()
		{
			this.Clear();
			this.AddRootNodes();
			this.InitializeRootNodes();
			this.AddSpecialFolders(rootNode);
			this.AddSpecialKeys(this.rootKey);
		}

		private void AddFile_Click(object sender, EventArgs e)
		{
			FilesView.Items.Clear();

			OpenFileDialog open_dialog = new OpenFileDialog();
			open_dialog.Multiselect = true;
            try
            {
                foreach (string file in (FilesTree.SelectedNode.Tag as FileTreeNode).Files)
                    FilesView.Items.Add(file);
            }
            catch { }

			open_dialog.ShowDialog();

			foreach (string filename in open_dialog.FileNames)
			{
				if (Path.GetExtension(filename).ToLower() == PLUGIN_EXTENSION)
					AddDLL(filename, FilesTree.SelectedNode, this.module);
				
				(FilesTree.SelectedNode.Tag as FileTreeNode).Files.Add(filename);
				FilesView.Items.Add(filename);
			}
		}

		private void FilesTree_KeyDown(object sender, KeyEventArgs e)
		{
			switch(e.KeyCode)
			{
				case Keys.Delete:
					RemoveSelectedNode();
					break;
				case Keys.F2:
					FilesTree.SelectedNode.BeginEdit();
					break;
			}
		}

		private void RegistryTree_KeyDown(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Delete:
					RemoveSelectedKey();
					break;
				case Keys.F2:
					RegistryTree.SelectedNode.BeginEdit();
					break;
			}
		}

		private void RegistryTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
		{
			if (e.Node == rootKey || e.Node.Parent == rootKey)
				e.Node.EndEdit(true);
		}
		
		private void FilesTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
		{
			if (e.Node == rootNode || e.Node.Parent == rootNode)
				e.Node.EndEdit(true);
		}

		private void FilesTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
		{
			if (e.Label != null)
				(e.Node.Tag as FileTreeNode).Name = e.Label;
		}

		private void RegistryTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
		{
			if (e.Label != null)
				(e.Node.Tag as RegTreeKey).Name = e.Label;
		}

		private void RegistryView_AfterLabelEdit(object sender, LabelEditEventArgs e)
		{
			(RegistryTree.SelectedNode.Tag as RegTreeKey).Properties[e.Item].Name = e.Label;
		}	

		private void ControlTab_SelectedIndexChanged(object sender, EventArgs e)
		{
			switch (ControlTab.SelectedIndex)
			{
				case 0:
					FilesTree.SelectedNode = FilesTree.Nodes[0];
					break;
				case 1:
					RegistryTree.SelectedNode = RegistryTree.Nodes[0];
					break;
				case 2:
					break;
			}
		}
		
		private void AddRegProperty(RegProperty.RegValueType type)
		{
			ListViewItem new_item = new ListViewItem("New property");
			new_item.SubItems.Add("");
			new_item.SubItems.Add(type.ToString());
			RegistryView.Items.Add(new_item);
			(RegistryTree.SelectedNode.Tag as RegTreeKey).Properties.Add(new RegProperty(new_item.Text, "", type));
			new_item.BeginEdit();
		}

		private void AddBinaryValue_Click(object sender, EventArgs e)
		{
			AddRegProperty(RegProperty.RegValueType.REG_BINARY);
		}

		private void AddStringValue_Click(object sender, EventArgs e)
		{
			AddRegProperty(RegProperty.RegValueType.REG_SZ);
		}

		private void AddExpString_Click(object sender, EventArgs e)
		{
			AddRegProperty(RegProperty.RegValueType.REG_EXPAND_SZ);
		}

		private void AddDWORDValue_Click(object sender, EventArgs e)
		{
			AddRegProperty(RegProperty.RegValueType.REG_DWORD);
		}

		private void AddMulStringValue_Click(object sender, EventArgs e)
		{
			AddRegProperty(RegProperty.RegValueType.REG_MULTI_SZ);
		}

		private void AddQWORDValue_Click(object sender, EventArgs e)
		{
			AddRegProperty(RegProperty.RegValueType.REG_QWORD);
		}

		private void RemoveSelectedFiles()
		{
			if (FilesView.SelectedItems.Count > 0)
				if (MessageBox.Show("Are you sure?", "Deleting", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
				{
					foreach (ListViewItem item in FilesView.SelectedItems)
					{
						item.Remove();
						(FilesTree.SelectedNode.Tag as FileTreeNode).RemoveFile(item.Text);
					}
				}
		}

		private void RemoveSelectedProperties()
		{
			if (RegistryView.SelectedItems.Count > 0)
				if (MessageBox.Show("Are you sure?", "Deleting", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
				{
					foreach (ListViewItem item in RegistryView.SelectedItems)
					{
						item.Remove();
						(RegistryTree.SelectedNode.Tag as RegTreeKey).RemoveProperty(new RegProperty(item.Text, item.SubItems[1].Text, RegProperty.GetTypeByName(item.SubItems[0].Text)));
					}
				}
		}

		private void filesView_KeyDown(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Delete:
					RemoveSelectedFiles();
					break;
			}
		}

		private void RegistryView_KeyDown(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Delete:
					RemoveSelectedProperties();
					break;
				case Keys.F2:
					if (RegistryView.SelectedItems.Count == 1)
						RegistryView.SelectedItems[0].BeginEdit();
					break;
			}
		}

		private void RegistryView_ItemActivate(object sender, EventArgs e)
		{
			if (RegistryView.SelectedItems.Count != 1)
				return;
			RegValueForm valueForm = new RegValueForm(RegistryView.SelectedItems[0].Text, RegistryView.SelectedItems[0].SubItems[1].Text);
			if (valueForm.ShowDialog() == DialogResult.OK)
			{
				RegistryView.SelectedItems[0].SubItems[1].Text = valueForm.value;
				(RegistryTree.SelectedNode.Tag as RegTreeKey).Properties[RegistryView.SelectedIndices[0]].Value = valueForm.value;
			}
		}

		public void Clear()
		{
			FilesTree.SelectedNode = FilesTree.Nodes[0];
			RegistryTree.SelectedNode = RegistryTree.Nodes[0];
			(FilesTree.SelectedNode.Tag as FileTreeNode).Clear();
			(RegistryTree.SelectedNode.Tag as RegTreeKey).Clear();

			FilesTree.Nodes.Clear();
			FilesView.Items.Clear();
			RegistryTree.Nodes.Clear();
			RegistryView.Items.Clear();			

			ControlTab.SelectedIndex = 0;	
		}

		public enum Trees
		{
			Files = 0,
			Registry = 1
		}	

		private void FilesTree_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button != MouseButtons.Right)
				return;
			
			TreeView view = sender as TreeView;
			if (view == null)
				return;

			TreeNode node = view.GetNodeAt(e.Location);
			if (node == null)
				return;

			view.SelectedNode = node;
		}

		private void AddFolder(string path)
		{
			TreeNode node = FilesTree.SelectedNode.Nodes.Add(Path.GetFileName(path));
			node.Tag = (FilesTree.SelectedNode.Tag as FileTreeNode).AddNode(Path.GetFileName(path));
			
			foreach (string file in Directory.GetFiles(path))
				(node.Tag as FileTreeNode).AddFile(file);
			
			foreach (string sub_dir in Directory.GetDirectories(path))
			{
				FilesTree.SelectedNode = node;				
				AddFolder(Path.GetFullPath(sub_dir));
			}		
		}

		private void existingFolderToolStripMenuItem_Click(object sender, EventArgs e)
		{
			FolderBrowserDialog fldDialog = new FolderBrowserDialog();
			fldDialog.ShowNewFolderButton = false;
			fldDialog.RootFolder = Environment.SpecialFolder.MyComputer;
			if (fldDialog.ShowDialog() == DialogResult.OK)
				AddFolder(fldDialog.SelectedPath);
		}									
	}
}
